package city.spring.modules.expense.service.impl;

import city.spring.modules.cardinfo.entity.CardNumberEntity;
import city.spring.modules.cardinfo.service.CardNumberService;
import city.spring.modules.checkinfee.entity.CheckinfeeEntity;
import city.spring.modules.checkinfee.repository.CheckinfeeRepository;
import city.spring.modules.checkinfee.service.CheckinfeeService;
import city.spring.modules.checkinfeeDetail.entity.CheckinfeeDetailEntity;
import city.spring.modules.checkinfeeDetail.repository.CheckinfeeDetailRepository;
import city.spring.modules.checkinfeeDetail.service.CheckinfeeDetailService;
import city.spring.modules.checkinfeeDetail.vo.CheckinfeeDetailVO;
import city.spring.modules.expense.entity.ExpenseRecordEntity;
import city.spring.modules.expense.repository.ExpenseRecordRepository;
import city.spring.modules.expense.service.ExpenseRecordService;
import city.spring.modules.expense.vo.ExpenseRecordEntityVO;
import city.spring.modules.oldman.entity.OldmanBedEntity;
import city.spring.modules.oldman.entity.OldmanInfoEntity;
import city.spring.modules.oldman.entity.OldmanLeaveApplyEntity;
import city.spring.modules.oldman.repository.OldmanLeaveApplyRepository;
import city.spring.modules.oldman.service.OldmanBedService;
import city.spring.modules.oldman.service.OldmanInfoService;
import city.spring.modules.oldman.service.OldmanLeaveApplyService;
import city.spring.modules.reception.entity.CheckInRecordEntity;
import city.spring.modules.reception.service.CheckInRecordService;
import city.spring.modules.system.entity.UserEntity;
import city.spring.modules.system.service.UserService;
import city.spring.utils.UserUtils;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.security.Principal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;

import static city.spring.utils.UserUtils.getPrincipal;

/**
 * Service：费用记录（缴费/消费）
 *
 * @author pxm
 * @date 2020-08-25 15:19:20
 */
@CacheConfig(cacheNames = {ExpenseRecordServiceImpl.CACHE_NAME})
@Transactional(rollbackFor = Exception.class)
@Service
public class ExpenseRecordServiceImpl extends ServiceImpl<ExpenseRecordRepository, ExpenseRecordEntity> implements ExpenseRecordService {
    private final CardNumberService cardNumberService;
    private final UserService userService;
    private final OldmanBedService oldmanBedService;
    private final CheckInRecordService checkInRecordService;
    private final OldmanLeaveApplyRepository oldmanLeaveApplyRepository;
    private final OldmanLeaveApplyService oldmanLeaveApplyService;
    private final CheckinfeeDetailRepository checkinfeeDetailRepository;
    private final CheckinfeeDetailService checkinfeeDetailService;
    private final OldmanInfoService oldmanInfoService;
    private final CheckinfeeRepository checkinfeeRepository;
    @Autowired
    private CheckinfeeService checkinfeeService;

    public ExpenseRecordServiceImpl(CardNumberService cardNumberService, UserService userService, OldmanBedService oldmanBedService,
                                    CheckInRecordService checkInRecordService, OldmanLeaveApplyRepository oldmanLeaveApplyRepository,
                                    OldmanLeaveApplyService oldmanLeaveApplyService, CheckinfeeDetailRepository checkinfeeDetailRepository,
                                    CheckinfeeService checkinfeeService, CheckinfeeDetailService checkinfeeDetailService, OldmanInfoService oldmanInfoService, CheckinfeeRepository checkinfeeRepository) {
        this.cardNumberService = cardNumberService;
        this.userService = userService;
        this.oldmanBedService = oldmanBedService;
        this.checkInRecordService = checkInRecordService;
        this.checkinfeeService = checkinfeeService;
        this.oldmanLeaveApplyRepository = oldmanLeaveApplyRepository;
        this.oldmanLeaveApplyService = oldmanLeaveApplyService;
        this.checkinfeeDetailRepository = checkinfeeDetailRepository;
        this.checkinfeeDetailService = checkinfeeDetailService;
        this.oldmanInfoService = oldmanInfoService;
        this.checkinfeeRepository = checkinfeeRepository;
    }

    @Override
    public void saveDepositRecord(ExpenseRecordEntity entity, @NonNull Principal user) {
        // 类型是充值还是消费在前端设置
        // 修改预存余额
        CardNumberEntity cardNumberEntity = cardNumberService.getByPersonId(entity.getOldmanId()); // 根据老人ID获取对应卡片信息记录
        BigDecimal a = cardNumberEntity.getDepositBalance(); // null
        if (a == null) {
            cardNumberEntity.setDepositBalance(entity.getMoney());
        } else {
            cardNumberEntity.setDepositBalance(a.add(entity.getMoney())); // 更新的预存金额=原先的预存余额+缴纳的金额
        }
        cardNumberService.updateCardNumber(cardNumberEntity);

        // 获取当前系统的操作人，设置预存缴费记录的登记人
        UserEntity userEntity = userService.getUserByIdOrAccount(user.getName());
        entity.setRegistrant(userEntity.getId());
        // 保存预存缴纳记录
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
    }

    @Override
    public void saveCardBalanceRecord(ExpenseRecordEntity entity, @NonNull Principal user) {
        // 类型是充值还是消费在前端设置
        // 修改余额
        CardNumberEntity cardNumberEntity = cardNumberService.getByPersonId(entity.getOldmanId()); // 根据老人ID获取对应卡片信息记录
        BigDecimal a = cardNumberEntity.getBalance(); // null
        if (a == null) {
            cardNumberEntity.setBalance(entity.getMoney());
        } else {
            cardNumberEntity.setBalance(a.add(entity.getMoney())); // 更新的余额=原先的余额+充值的金额
        }
        cardNumberService.updateCardNumber(cardNumberEntity);

        // 获取当前系统的操作人，设置登记人
        UserEntity userEntity = userService.getUserByIdOrAccount(user.getName());
        entity.setRegistrant(userEntity.getId());
        // 保存一卡通费用记录
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
    }

    @Override
    public void saveOtherConsumptionRecord(ExpenseRecordEntity entity, @NonNull Principal user) {
        // 获取当前系统的操作人，设置预存缴费记录的登记人
        UserEntity userEntity = userService.getUserByIdOrAccount(user.getName());
        entity.setRegistrant(userEntity.getId());
        // 类型（缴费/消费）
        entity.setType("消费");
        // 类别：其他消费
        entity.setCategory("其他消费");
        // 保存 其他消费 消费记录
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
    }

    @Override
    public void saveWaterOrElectricRecord(ExpenseRecordEntity entity, @NonNull Principal user) {
        // 获取当前系统的操作人，设置预存缴费记录的登记人
        UserEntity userEntity = userService.getUserByIdOrAccount(user.getName());
        entity.setRegistrant(userEntity.getId());
        // 类型（缴费/消费）
        entity.setType("消费");
        // 类别
        entity.setCategory("水电消费");
        // 保存 水电消费 消费记录
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
    }

    @Override
    public void saveExpenseRecord(ExpenseRecordEntity entity) {
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
    }

    @Override
    public void updateExpenseRecord(ExpenseRecordEntity entity) {
        // 使用 updateById(entity); 修改数据时，将会修改 entity 对象中所有非null数据，如果某个字段为null，将会忽略该字段的修改
        if (!updateById(entity)) {
            throw new RuntimeException("修改信息失败");
        }
    }

    @Override
    public void deleteExpenseRecord(String primaryKey) {
        removeById(primaryKey);
    }

    @Override
    public void deleteExpenseRecord(List<String> primaryKeys) {
        removeByIds(primaryKeys);
    }

    @Override
    public IPage<ExpenseRecordEntityVO> depositListPage(Page<? extends ExpenseRecordEntity> page, Wrapper<? extends ExpenseRecordEntity> wrapper) {
        return baseMapper.depositListPage(page, wrapper);
    }

    @Override
    public void saveCheckinFee(ExpenseRecordEntity entity) {
        entity.setHappenTime(new Date());
        // 类型（缴费/消费）
        entity.setType("缴费");
        // 类别
        entity.setCategory("入院缴费");
        // 登记人（当前登录用户）
        UserEntity userEntity = userService.getUserByIdOrAccount(getPrincipal().getName());
        entity.setRegistrant(userEntity.getId());
        // 结算状态 ？？？
        // 状态 ？？？？？
        // 预交费用 money
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
        // 修改入院收费记录状态
        CheckinfeeEntity checkinfeeEntity = checkinfeeService.getById(entity.getBillId());
        checkinfeeEntity.setStatus(1);
        // 收费员
        checkinfeeEntity.setUserId(UserUtils.getPrincipal().getName());
        checkinfeeService.lambdaUpdate()
                .set(CheckinfeeEntity::getStatus, checkinfeeEntity.getStatus())
                .set(CheckinfeeEntity::getUserId, checkinfeeEntity.getUserId())
                .eq(CheckinfeeEntity::getId, checkinfeeEntity.getId())
                .update();

        // 修改入院缴费明细记录状态
        List<CheckinfeeDetailEntity> checkinfeeDetailByCheckinfees = checkinfeeDetailService.getCheckinfeeDetailByCheckinfeeId(checkinfeeEntity.getId());
        checkinfeeDetailByCheckinfees.forEach(checkinfeeDetail -> {
            checkinfeeDetail.setStatus(1);
            checkinfeeDetailService.lambdaUpdate()
                    .set(CheckinfeeDetailEntity::getStatus, checkinfeeDetail.getStatus())
                    .eq(CheckinfeeDetailEntity::getId, checkinfeeDetail.getId())
                    .update();
        });

        // 预交余额大于0时，生成一条预存缴费记录
        if (entity.getMoney().compareTo(BigDecimal.ZERO) == 1) {
            ExpenseRecordEntity expenseRecordEntity = new ExpenseRecordEntity();
            expenseRecordEntity.setOldmanId(checkinfeeEntity.getOldmanId());
            OldmanBedEntity oldmanBedEntity = oldmanBedService.getBedInfoByoldmanId(checkinfeeEntity.getOldmanId());
            expenseRecordEntity.setBedNum(oldmanBedEntity.getBedId());
            expenseRecordEntity.setMoney(entity.getMoney());
            expenseRecordEntity.setHappenTime(new Date());
            expenseRecordEntity.setPayer(entity.getPayer());
            expenseRecordEntity.setType("缴费");
            expenseRecordEntity.setCategory("预存交费");
            expenseRecordEntity.setPayType(entity.getPayType());
            expenseRecordEntity.setRegistrant(entity.getRegistrant());
            expenseRecordEntity.setRegistrant(entity.getRegistrant());
            expenseRecordEntity.setRemarks("入院缴费时，用户缴费大于应交费用，剩余费用作为预存余额");
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.LOCATION, String.valueOf(expenseRecordEntity.getId()));
            save(expenseRecordEntity);

            // 修改card预存余额
            CardNumberEntity cardNumberEntity = cardNumberService.getByPersonId(checkinfeeEntity.getOldmanId());
            cardNumberEntity.setDepositBalance(expenseRecordEntity.getMoney());
            cardNumberService.lambdaUpdate()
                    .set(CardNumberEntity::getDepositBalance, cardNumberEntity.getDepositBalance())
                    .eq(CardNumberEntity::getId, cardNumberEntity.getId())
                    .update();
        } else {
            // 修改card预存余额
            CardNumberEntity cardNumberEntity = cardNumberService.getByPersonId(checkinfeeEntity.getOldmanId());
            cardNumberEntity.setDepositBalance(entity.getMoney());
            cardNumberService.lambdaUpdate()
                    .set(CardNumberEntity::getDepositBalance, cardNumberEntity.getDepositBalance())
                    .eq(CardNumberEntity::getId, cardNumberEntity.getId())
                    .update();
        }
    }

    @Override
    public void saveMonthFee(ExpenseRecordEntity entity) {
        // 交月入住费用不用预存抵扣时，只生成一个费用记录
        // 用预存抵扣时，生成费用记录、预存抵扣的费用记录、预存余额的更新
        entity.setHappenTime(new Date());
        // 登记人（当前登录用户）
        UserEntity userEntity = userService.getUserByIdOrAccount(getPrincipal().getName());
        entity.setRegistrant(userEntity.getId());
        // 结算状态 ？？？
        // 状态 ？？？？？
        // 预交费用 money
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
        if (entity.getCategory().equals("预存")) {
            // 修改card预存余额
            CardNumberEntity cardNumberEntity = cardNumberService.getByPersonId(entity.getOldmanId());
            cardNumberEntity.setDepositBalance(cardNumberEntity.getDepositBalance().subtract(entity.getActualfee()));
            cardNumberService.lambdaUpdate()
                    .set(CardNumberEntity::getDepositBalance, cardNumberEntity.getDepositBalance())
                    .eq(CardNumberEntity::getId, cardNumberEntity.getId())
                    .update();
        }
    }

    @Override
    public List<ExpenseRecordEntityVO> getOnesMonthPayRecords(String personId) {
        return baseMapper.getOnesMonthPayRecords(personId);
    }

    @Override
    public void saveLeaveSettlement(ExpenseRecordEntity entity, Date leaveDate, Date monthEnd, Integer invalidState) {
        // 1、增加结算记录
        entity.setHappenTime(new Date());
        // 类别
        entity.setCategory("退住结算");
        // 登记人（当前登录用户）
        UserEntity userEntity = userService.getUserByIdOrAccount(getPrincipal().getName());
        entity.setRegistrant(userEntity.getId());
        // 结算状态 ？？？
        // 状态 ？？？？？
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
        // 2、在入住记录表  补上退住日期（老人最新的入住记录）
        CheckInRecordEntity checkInRecordEntity = checkInRecordService.getLatestRecordByOldmanId(entity.getOldmanId());
        checkInRecordEntity.setCheckOutTime(leaveDate);
        checkInRecordService.saveOrUpdate(checkInRecordEntity);
        // 3、结算后 老人的预存余额变为0 (暂不置0，结算打印和单据打印时，若为0,则计算不准；如重新入住，再置0)
//        CardNumberEntity cardNumberEntity = cardNumberService.getByPersonId(entity.getOldmanId());
//        cardNumberEntity.setDepositBalance(BigDecimal.ZERO);
//        cardNumberService.updateCardNumber(cardNumberEntity);
        // 4、结算申请 的处理状态 变为 1：已处理
        OldmanLeaveApplyEntity oldmanLeaveApplyEntity = oldmanLeaveApplyRepository.getByOldmanId(entity.getOldmanId());
        oldmanLeaveApplyEntity.setHandleStatus(1);
        oldmanLeaveApplyService.updateOldmanLeaveApply(oldmanLeaveApplyEntity);
        // 5、老人的入住状态改为“退住”
        OldmanInfoEntity oldmanInfoEntity = oldmanInfoService.getById(entity.getOldmanId());
        oldmanInfoEntity.setStatus(3);
        oldmanInfoService.updateOldmanInfo(oldmanInfoEntity);
        // 6、去重，状态失效：退住如果没有住够一个月，要重新计算这个月的费用，先前的该月的交费记录失效（即作废）
        if (invalidState == 1) {
            // 退住月之后的记录记失效状态
            checkinfeeRepository.updateInvalidState1(entity.getOldmanId(), leaveDate);
        }
        if (invalidState == 2) {
            // 旧退住月及之后月的记失效状态
            checkinfeeRepository.updateInvalidState2(entity.getOldmanId(), monthEnd);
        }

    }

    @Override
    public IPage<ExpenseRecordEntityVO> getSettlementRecords(Page<? extends ExpenseRecordEntity> page, Wrapper<? extends ExpenseRecordEntity> wrapper) {
        return baseMapper.getSettlementRecords(page, wrapper);
    }

    @Override
    public ExpenseRecordEntityVO getLatestSettlementRecord(String oldmanId) {
        return baseMapper.getLatestSettlementRecord(oldmanId);
    }

    @Override
    public ExpenseRecordEntityVO getSettlementRecord(String id) {
        return baseMapper.getSettlementRecord(id);
    }

    @Override
    public void checkSettlement(ExpenseRecordEntity entity, @NonNull Principal user) {
        // 审核人
        UserEntity userEntity = userService.getUserByIdOrAccount(getPrincipal().getName());
        entity.setChecker(userEntity.getId());
        // 审核时间
        entity.setCheckTime(LocalDateTime.now());
        if (!updateById(entity)) {
            throw new RuntimeException("操作失败！");
        }

    }

    @Override
    public List<ExpenseRecordEntityVO> getBillDetailsBetween2Date(String expecseId) {
        return baseMapper.getBillDetailsBetween2Date(expecseId);
    }

    @Override
    public Object getSettlementPrintInfo(String expenseId) {
        ExpenseRecordEntityVO expenseRecordEntityVO = baseMapper.getSettlementPrintInfo(expenseId);
        CheckinfeeDetailVO checkinfeeDetailVO = checkinfeeDetailRepository.getDepositAndBalance(expenseRecordEntityVO.getOldmanId());
        expenseRecordEntityVO.setDepositMount(checkinfeeDetailVO.getDepositMount());
        expenseRecordEntityVO.setBalanceMount(checkinfeeDetailVO.getBalanceMount());
        return expenseRecordEntityVO;
    }

    @Override
    public List<ExpenseRecordEntityVO> getBillPrintDetail(String expenseId) {
        // 入住月费收费账单记录集合
        List<ExpenseRecordEntityVO> records = baseMapper.getBillPrintDetail(expenseId);
        // 加上 退还押金
        BigDecimal deposit = (BigDecimal) baseMapper.getDepositByExpenseId(expenseId);
        ExpenseRecordEntityVO exvo = new ExpenseRecordEntityVO();
        exvo.setId("tuifeiid");
        exvo.setType("退费");
        exvo.setMonthfeeType("押金");
        exvo.setReceivableThisMonth(deposit.negate());
        exvo.setDescribes("退还 押金");
        records.add(exvo);
        // 加上 预存余额（有余额：退费  余额为负/0：交费）
        BigDecimal balance = baseMapper.getDepositBalanceByExpenseId(expenseId);
        ExpenseRecordEntityVO balancevo = new ExpenseRecordEntityVO();
        if (balance.compareTo(BigDecimal.ZERO) == 1 || balance.compareTo(BigDecimal.ZERO) == 0) {
            balancevo.setId("yucunyueid");
            balancevo.setType("退费");
            balancevo.setMonthfeeType("预存余额");
            balancevo.setReceivableThisMonth(balance.negate());
            balancevo.setDescribes("退还 预存余额 ");
            records.add(balancevo);
        } else {
            balancevo.setId("yucunyueid");
            balancevo.setType("收费");
            balancevo.setMonthfeeType("预存余额");
            balancevo.setReceivableThisMonth(balance.negate());
            balancevo.setDescribes("补交 预存欠费 ");
            records.add(balancevo);
        }
        return records;
    }
}